﻿\subsection{x64: \Optimizing MSVC 2013}

\lstinputlisting[caption=\Optimizing MSVC 2013 x64,style=customasmx86]{\CURPATH/MSVC2013_x64_Ox_RU.asm}

В начале, MSVC вставил тело функции \strlen{} прямо в код, потому что решил, что так будет
быстрее чем обычная работа \strlen{} + время на вызов её и возврат из нее.

Это также называется \IT{inlining}: \myref{inline_code}.

\myindex{x86!\Instructions!OR}
\myindex{\CStandardLibrary!strlen()}
\label{using_OR_instead_of_MOV}
Первая инструкция функции \strlen{} вставленная здесь,\\
это \TT{OR RAX, 0xFFFFFFFFFFFFFFFF}. 
MSVC часто использует \TT{OR} вместо \TT{MOV RAX, 0xFFFFFFFFFFFFFFFF}, потому что опкод получается короче.

И конечно, это эквивалентно друг другу: все биты просто выставляются, а все выставленные
биты это -1 в дополнительном коде (two's complement): \myref{sec:signednumbers}.

Кто-то мог бы спросить, зачем вообще нужно использовать число -1 в функции \strlen{}?

Вследствие оптимизации, конечно.
Вот что сделал MSVC:

\lstinputlisting[caption=Вставленная \strlen{} сгенерированная MSVC 2013 x64,style=customasmx86]{\CURPATH/strlen_MSVC_RU.asm}

Попробуйте написать короче, если хотите инициализировать счетчик нулем!

Ну, например:

\lstinputlisting[caption=Наша версия \strlen{},style=customasmx86]{\CURPATH/my_strlen_RU.asm}

Не получилось. Нам придется вводить дополнительную инструкцию JMP!

Что сделал MSVC 2013, так это передвинул инструкцию \TT{INC} в место перед загрузкой символа.

Если самый первый символ --- нулевой, всё нормально, \RAX содержит 0 в этот момент, так что
итоговая длина строки будет 0.

Остальную часть функции проще понять.

\subsection{x64: \NonOptimizing GCC 4.9.1}

\lstinputlisting[style=customasmx86]{\CURPATH/GCC491_x64_O0_RU.asm}

Комментарии автора.
После исполнения \strlen{}, управление передается на метку L2,
и там проверяются два выражения, одно после другого.

\myindex{\CLanguageElements!Short-circuit}
Второе никогда не будет проверяться, если первое выражение не истинно (\IT{str\_len==0})
(это \q{short-circuit}).

Теперь посмотрим на эту функцию в коротком виде:

\begin{itemize}
\item Первая часть for() (вызов \strlen{})
\item goto L2
\item L5: Тело for(). переход на выход, если нужно
\item Третья часть for() (декремент str\_len)
\item L2: Вторая часть for(): проверить первое выражение, затем второе. 
переход на начало тела цикла, или выход.

\item L4: // выход
\item return s
\end{itemize}

\subsection{x64: \Optimizing GCC 4.9.1}
\label{string_trim_GCC_x64_O3}

\lstinputlisting[style=customasmx86]{\CURPATH/GCC491_x64_O3_RU.asm}

Тут более сложный результат.
Код перед циклом исполняется только один раз, но также содержит проверку символов \CRLF{}!

Зачем нужна это дублирование кода?

Обычная реализация главного цикла это, наверное, такая:

\begin{itemize}
\item (начало цикла) проверить символы \CRLF{}, принять решения

\item записать нулевой символ
\end{itemize}

Но GCC решил поменять местами эти два шага. 
Конечно, шаг \IT{записать нулевой символ} не может быть первым, так что нужна еще одна
проверка:


\begin{itemize}
\item обработать первый символ. сравнить его с \CRLF{}, выйти если символ не равен \CRLF{}

\item (начало цикла) записать нулевой символ

\item проверить символы \CRLF{}, принять решения

\end{itemize}

Теперь основной цикл очень короткий, а это очень хорошо для современных процессоров.

Код не использует переменную str\_len, но str\_len-1.

Так что это больше похоже на индекс в буфере.
Должно быть, GCC заметил, что выражение str\_len-1 используется дважды.

Так что будет лучше выделить переменную, которая всегда содержит значение равное 
текущей длине строки минус 1, и уменьшать его на 1 (это тот же эффект, что и уменьшать
переменную str\_len).

